/*
 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.presentation.rmi;

import java.lang.reflect.Field;

import java.util.Hashtable;

import javax.naming.*;
import javax.naming.spi.StateFactory;

import java.security.AccessController;
import java.security.PrivilegedAction;

import javax.rmi.PortableRemoteObject;

import com.sun.corba.se.spi.orb.ORB;

import java.rmi.Remote;
import java.rmi.server.ExportException;

// XXX This creates a dependendcy on the implementation
// of the CosNaming service provider.
import com.sun.jndi.cosnaming.CNCtx;

import com.sun.corba.se.spi.presentation.rmi.StubAdapter;

/**
 * StateFactory that turns java.rmi.Remote objects to org.omg.CORBA.Object.
 * This version works either with standard RMI-IIOP or Dynamic RMI-IIOP.
 * Based on the original com.sun.jndi.cosnaming.RemoteToCorba and
 * com.sun.jndi.toolkit.corba.CorbaUtils.
 *
 * @author Ken Cavanaugh
 */

public class JNDIStateFactoryImpl implements StateFactory {

  private static final Field orbField;

  static {
    orbField = (Field) AccessController.doPrivileged(
        new PrivilegedAction() {
          public Object run() {
            Field fld = null;
            try {
              Class cls = CNCtx.class;
              fld = cls.getDeclaredField("_orb");
              fld.setAccessible(true);
            } catch (Exception exc) {
              // XXX log exception at FINE
            }
            return fld;
          }
        }
    );
  }

  public JNDIStateFactoryImpl() {
  }

  /**
   * Returns the CORBA object for a Remote object.
   * If input is not a Remote object, or if Remote object uses JRMP, return null.
   * If the RMI-IIOP library is not available, throw ConfigurationException.
   *
   * @param orig The object to turn into a CORBA object. If not Remote, or if is a JRMP stub or
   * impl, return null.
   * @param name Ignored
   * @param ctx The non-null CNCtx whose ORB to use.
   * @param env Ignored
   * @return The CORBA object for <tt>orig</tt> or null.
   * @throws ConfigurationException If the CORBA object cannot be obtained due to configuration
   * problems
   * @throws NamingException If some other problem prevented a CORBA object from being obtained from
   * the Remote object.
   */
  public Object getStateToBind(Object orig, Name name, Context ctx,
      Hashtable<?, ?> env) throws NamingException {
    if (orig instanceof org.omg.CORBA.Object) {
      return orig;
    }

    if (!(orig instanceof Remote))
    // Not for this StateFactory
    {
      return null;
    }

    ORB orb = getORB(ctx);
    if (orb == null)
    // Wrong kind of context, so just give up and let another StateFactory
    // try to satisfy getStateToBind.
    {
      return null;
    }

    Remote stub = null;

    try {
      stub = PortableRemoteObject.toStub((Remote) orig);
    } catch (Exception exc) {
      // XXX log at FINE level?
      // Wrong sort of object: just return null to allow another StateFactory
      // to handle this.  This can happen easily because this StateFactory
      // is specified for the application, not the service context provider.
      return null;
    }

    if (StubAdapter.isStub(stub)) {
      try {
        StubAdapter.connect(stub, orb);
      } catch (Exception exc) {
        if (!(exc instanceof java.rmi.RemoteException)) {
          // XXX log at FINE level?
          // Wrong sort of object: just return null to allow another StateFactory
          // to handle this call.
          return null;
        }

        // ignore RemoteException because stub might have already
        // been connected
      }
    }

    return stub;
  }

  // This is necessary because the _orb field is package private in
  // com.sun.jndi.cosnaming.CNCtx.  This is not an ideal solution.
  // The best solution for our ORB is to change the CosNaming provider
  // to use the StubAdapter.  But this has problems as well, because
  // other vendors may use the CosNaming provider with a different ORB
  // entirely.
  private ORB getORB(Context ctx) {
    ORB orb = null;

    try {
      orb = (ORB) orbField.get(ctx);
    } catch (Exception exc) {
      // XXX log this exception at FINE level
      // ignore the exception and return null.
      // Note that the exception may be because ctx
      // is not a CosNaming context.
    }

    return orb;
  }
}
